﻿/*
   RxInputManager
   Copyright 2011 Jordan Neumeyer

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
   
   File: RxGamePad.cs 
 */

using System;
using System.Collections.Generic;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

using RxInputManager.Extensions;
using RxInputManager.Interfaces;

namespace RxInputManager.Devices
{
    class RxGamePad : RxDevice<GamePadState>, IRxGamePad
    {
        public IObservable<GamePadState> isConnected;

        public RxGamePad(PlayerIndex playerID)
            : this(playerID, RxInputScheduler.Scheduler)
        { }

        public RxGamePad(PlayerIndex playerID, IScheduler scheduler) : base(scheduler)
        {
            PlayerID = playerID;
            DeviceName = String.Format("Controller {0}", PlayerID);
            isConnected = subject.AsObservable()
                                 //.ObserveOn(scheduler)
                                 .DistinctUntilChanged(pad => pad.IsConnected);

            baseEvent = subject.Where(pad => pad.IsConnected)
                               //.ObserveOn(scheduler)
                               .Publish();

            if (CurrentState.IsConnected)
            {
                disposed = baseEvent.Connect();
            }

            isConnected.Where(state => state.IsConnected == false).Subscribe(_ => { DisableEvents(); Console.WriteLine("{0} controller disconnected!", DeviceName); isAttached = false; });
            isConnected.Where(state => state.IsConnected == true).Subscribe(_ => { EnableEvents(); Console.WriteLine("{0} controller connected!", DeviceName); isAttached = true; });
            
        }

        private Buttons? ActivateKey = null;
        public IObservable<bool> ControllerActivated(Buttons? key = null)
        {
            if (null != key)
            {
                ActivateKey = key;
            }

            if (null != ActivateKey)
            {
                return (isConnected = isConnected.Where(state => state.IsButtonDown(key.Value))).Select(state => state.IsConnected);
            }
            return isConnected.Select(state => state.IsConnected);
        }

        protected PlayerIndex PlayerID;
        protected override GamePadState CurrentState 
        {
            get { return GamePad.GetState(PlayerID); }
        }

        private bool isAttached;
        public override bool IsAttached
        {
            get { return isAttached; }
        }

        public IObservable<GamePadState> ButtonPressed(Buttons button)
        {
            return baseEvent.ButtonPressed(button);
        }

        // maybe a TakeWhile or skip until
        public IObservable<GamePadState> ButtonPressedOnce(Buttons button)
        {
            return baseEvent.Where(state => previousState.IsButtonUp(button))
                            .Where(state => state.IsButtonDown(button));
        }

        public IObservable<GamePadState> ButtonReleased(Buttons button)
        {
            return baseEvent.ButtonReleased(button);
        }

        public IObservable<GamePadState> ButtonHeld(Buttons button, int delayTime = RxInputManager.Constants.DelayTime)
        {
            return baseEvent.ButtonHeld(button, delayTime);
        }

        public IObservable<GamePadState> ButtonsPressed(HashSet<Buttons> buttons)
        {
            return baseEvent.ButtonsPressed(buttons);
        }

        // maybe use TakeWhile or something
        //public static IObservable<GamePadState> ButtonsPressedOnce(this IObservable<GamePadState> src, HashSet<Buttons> buttons)
        //{
        //    return src.Where(_ => !buttons.Overlaps(previousState.GetPressedButtons()))
        //                    .Where(state => buttons.Overlaps(state.GetPressedButtons()));
        //}

        public IObservable<GamePadState> ButtonsPressedOnce(HashSet<Buttons> buttons)
        {
            return baseEvent.Where(_ => !buttons.Overlaps(previousState.GetPressedButtons()) )
                            .Where(state => buttons.Overlaps(state.GetPressedButtons()));
        }

        public IObservable<GamePadState> ButtonsHeld(HashSet<Buttons> buttons, int delay = Constants.DelayTime)
        {
            return baseEvent.ButtonsHeld(buttons, delay);
        }

        public IObservable<GamePadState> Sticks(Buttons stick, int threshold = Constants.StickThreshold)
        {
          //  if (stick != Buttons.LeftStick || stick != Buttons.RightStick)
            //    throw new KeyNotFoundException(String.Format("{0} is not the left or right stick", stick));

            return baseEvent.Sticks(stick, threshold);
                                                
        }

        public IObservable<GamePadState> RawState
        {
            get
            {
                return baseEvent;
            }
        }

        public void SetPlayerID(PlayerIndex playerID)
        {
            this.PlayerID = playerID;
        }
    }
}
